from datetime import datetime, timedelta
from random import randint

import numpy as np
import pandas as pd
import time
from sqlalchemy import create_engine


def generate_xid():
    nano = time.perf_counter_ns()
    return datetime.now().strftime('%Y%m%d%H%M%S') + str(int(nano % 10e5))


def test_generate_xid():
    for _ in range(100):
        print(generate_xid())


def random_time(start, end):
    def nanoseconds_to_time(ns):
        HHMMSS = ns // 10 ** 7
        HH = HHMMSS // 3600
        MM = (HHMMSS // 60) % 60
        SS = HHMMSS % 60
        sssnnnn = ns % 10 ** 7
        return HH * 10 ** 11 + MM * 10 ** 9 + SS * 10 ** 7 + sssnnnn

    def time_to_nanoseconds(t):
        # HHMMSSsssnnnn => 小时、分钟、秒、毫秒、微秒、纳秒
        HH = t // 10 ** 11
        MM = (t // 10 ** 9) % 100
        SS = (t // 10 ** 7) % 100
        sssnnnn = t % 10 ** 7
        return ((((HH * 60) + MM) * 60) + SS) * 10 ** 7 + sssnnnn

    start_ns = time_to_nanoseconds(start)
    end_ns = time_to_nanoseconds(end)
    if start_ns >= end_ns:
        return None  # 起始时间必须小于结束时间
    random_ns = np.random.randint(start_ns, end_ns)
    return nanoseconds_to_time(random_ns)


def random_chunk(qty, n, low_percent=100, high_percent=101):
    if qty < 6:
        return [qty]
    if qty / n <= 2:
        return random_chunk(qty, 2, low_percent, high_percent)
    qty = np.floor((np.random.randint(low_percent, high_percent) / 100) * qty)
    qty -= n  # 保证每个子单至少有1手
    # 生成 n 个随机数
    random_numbers = np.random.rand(n)
    # 计算比例
    proportions = random_numbers / sum(random_numbers)
    # 按比例分配 Qty
    parts = np.floor(proportions * qty).astype(int)
    # 调整最后一个元素以确保总和等于 Qty
    parts[-1] = qty - sum(parts[:-1])
    parts += 1
    return parts


def test_random_chunk():
    print(random_chunk(100, 5, 100, 101))


def oneMother(XID, config, arrivalTime):
    oneMother = []
    mothers = pd.DataFrame(columns=config['mother_columns'])
    motherQty = np.random.randint(6, 100)  # 委托数量
    # print('mother',motherQty)
    # 生成一个母单，写入数据库，并return 母单
    oneMother.extend([XID,  # 母单ID
                      config['Strategy'],  # 算法
                      config['SecurityID'],  # 证券ID
                      np.random.choice(['Buy', 'Sell']),  # 买卖方向
                      motherQty,  # 委托数量
                      ])
    startTime = arrivalTime + config['delay']
    endTime = startTime + np.random.randint(1, 30) * 10 ** 9  # 随机1，30分钟
    oneMother.extend([startTime, endTime])  # 开始时间，结束时间
    mothers.loc[len(mothers)] = oneMother
    return mothers


def generate_mother(**kwargs):
    qty = randint(kwargs['min_qty'], kwargs['max_qty'])
    # print('qty=', qty)
    xid = generate_xid()
    df = pd.DataFrame(
        [[xid, 'sep', kwargs['Security_id'], kwargs['side'], qty, kwargs['start_time'], kwargs['end_time']]],
        columns=['M_XID', 'Strategy', 'SecurityID', 'Side', 'M_Qty', 'StartTime', 'EndTime'])
    print(df)
    return df


def test_generate_mother():
    generate_mother(min_qty=10, max_qty=100, Security_id='601398', side='buy', start_time='202409091402',
                    end_time='202409091430')


def generate_child(m_xid, qty_mother, start_time_mother_str, end_time_mother_str):
    start_time_mother = datetime.strptime(start_time_mother_str, '%Y%m%d%H%M%S')
    end_time_mother = datetime.strptime(end_time_mother_str, '%Y%m%d%H%M%S')
    second = (end_time_mother - start_time_mother).total_seconds()

    child_count = randint(2, 10)

    child_qty_s = random_chunk(qty_mother, child_count)
    child_seconds = random_chunk(second, child_count)

    child_min_count = min(len(child_qty_s), len(child_seconds))
    child_qty_s, child_seconds = [], []

    child_qty_s = random_chunk(qty_mother, child_min_count)
    child_seconds = random_chunk(second, child_min_count)

    child_seconds.sort()

    data = []
    for i, child_qty in enumerate(child_qty_s):
        child_time = start_time_mother + timedelta(seconds=int(child_seconds[i]))
        data.append([generate_xid(), m_xid, child_qty, child_time.strftime('%Y%m%d%H%M%S')])

    df = pd.DataFrame(data, columns=['C_XID', 'Mother_XID', 'C_Qty', 'TransactTime'])
    print(df)
    return df


def test_generate_child():
    start_time = '20240909140200'
    end_time = '20240909143000'

    df = generate_mother(min_qty=10, max_qty=100, Security_id='601398', side='buy', start_time=start_time,
                         end_time=end_time)
    if df is None:
        return
    generate_child(df['M_XID'][0], df['M_Qty'][0], start_time, end_time)


def children(config, mother):
    numerOfChildren = np.random.randint(2, 10)
    childrenQty = random_chunk(mother['M_Qty'].item(), numerOfChildren, 90, 101)
    numerOfChildren = len(childrenQty)
    # print('child',childrenQty)
    dddd = np.full((numerOfChildren, len(config['child_columns'])), np.nan)
    children = pd.DataFrame(dddd, columns=config['child_columns'])
    # 根据母单生产子单，写入数据库，并return 子单
    children['C_XID'] = np.arange(0, numerOfChildren)
    children['Mother_XID'] = mother['M_XID'].item()
    children['SecurityID'] = mother['SecurityID'].item()
    children['Side'] = mother['Side'].item()
    children['C_Qty'] = childrenQty
    children['TransacationTime'] = [random_time(mother['StartTime'].item(), mother['EndTime'].item())
                                    for _ in range(numerOfChildren)]
    return children


def random_Px(numberOfExe, config):
    prices = np.random.normal(loc=config['mean'], scale=config['std'], size=numberOfExe)
    prices_clipped = np.clip(config['minPX'], config['maxPx'], prices)
    prices_formatted = np.round(prices_clipped, 5)
    return prices_formatted


def ExeRepos(children, config):
    exeRepos = pd.DataFrame()
    # 执行子单，写入数据库，并return 执行结果
    for i in range(len(children)):
        numerOfExe = np.random.randint(1, 5)
        exeQty = random_chunk(children.loc[i, 'C_Qty'], numerOfExe, 100, 101)
        numerOfExe = len(exeQty)
        # print('exe',exeQty)
        dddd = np.full((numerOfExe, len(config['report_columns'])), np.nan)
        one_exeRepo = pd.DataFrame(dddd, columns=config['report_columns'])
        # 根据母单生产子单，写入数据库，并return 子单
        one_exeRepo['Child_XID'] = children.loc[i, 'C_XID']
        one_exeRepo['SecurityID'] = children.loc[i, 'SecurityID']
        one_exeRepo['Side'] = children.loc[i, 'Side']
        one_exeRepo['LastPx'] = random_Px(numerOfExe, config)
        one_exeRepo['LastQty'] = exeQty
        one_exeRepo['ExeTime'] = children.loc[i, 'TransacationTime'] + 10 ** 7
        exeRepos = pd.concat([exeRepos, one_exeRepo], axis=0)
    return exeRepos


# 读取配置函数
def readConfig():
    config = {}
    # 母单配置
    config['mother_columns'] = ['M_XID', 'Strategy', 'SecurityID', 'Side', 'M_Qty', 'StartTime', 'EndTime']
    config['Strategy'] = '卡方vwap_plus'
    config['SecurityID'] = '002930'
    config['delay'] = 10 ** 7  # 一秒
    # 子单配置
    config['child_columns'] = ['C_XID', 'Mother_XID', 'SecurityID', 'Side', 'C_Qty', 'TransacationTime']
    # 执行结果配置
    config['report_columns'] = ['Child_XID', 'SecurityID', 'Side', 'LastPx', 'LastQty', 'ExeTime']
    config['mean'] = 10.65  # 假设价格均值为100
    config['std'] = 0.05  # 假设价格标准差为10
    config['maxPx'] = 10.8  # 假设价格上限为10.8
    config['minPX'] = 10.5  # 假设价格下限为10.5
    # 实时入库配置
    config['engine'] = 'mysql+pymysql://root:oz010419@localhost:3306/tca_hanlong'
    config['start_time'] = 1000000000000
    config['test_interval'] = 10 ** 7
    config['duration'] = 1 * 60 * 5  # 5分钟(实际上是次数）
    return config


# 测试
# config = readConfig()
# mother = oneMother(1,config,1000000000000)
# print(mother)
# testchild = children(config,mother)
# print(testchild)
# testexe = ExeRepos(testchild,config)
# print(testexe)

# 实时入库
def run_data(config):
    engine = create_engine(config['engine'])
    print('数据库连接成功！')
    print('入库开始！')
    for count in range(config['duration']):
        momS = oneMother(count, config, config['start_time'] + count * config['test_interval'])
        childrenS = children(config, momS)
        exeS = ExeRepos(childrenS, config)

        print('第{}次'.format(count + 1))
        print('mom\n', momS)
        print('child\n', childrenS)
        print('exe\n', exeS)

        momS.to_sql('MOrd', con=engine, if_exists='append', index=False)
        childrenS.to_sql('COrd', con=engine, if_exists='append', index=False)
        exeS.to_sql('ExeRepo', con=engine, if_exists='append', index=False)
        time.sleep(1)


# config = readConfig()
# run_data(config)

def test_time():
    for _ in range(100):
        nano = time.perf_counter_ns()
        print(datetime.now().strftime('%Y%m%d%H%M%S') + str(int(nano % 10e5)))
